//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.UIDesigner
{
    #region Using Directives
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Configuration;
    using System.Diagnostics;
    using System.IO;
    using System.Text;
    using System.Windows;
    using System.Workflow.ComponentModel.Compiler;
    using System.Xml;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities.Properties;
    using Microsoft.Research.ScientificWorkflow.UIDesigner.Import;
    using TridentComposer;
    using SR = Microsoft.Research.DataLayer;
    using Microsoft.Practices.EnterpriseLibrary.Logging;
    using TridentAPI;
    using Microsoft.Research.DataLayer;
    using System.Collections;
    using System.Linq;
    using System.ComponentModel;
    using System.Globalization;

    #endregion
    
    /// <summary>
    /// Holds the Enumerations for WorkflowStatus in the Composer
    /// </summary>
    public enum WorkflowStatus
    {
        NewWorkflow,
        OpenedFromRegistry,
        OpenedFromFileSystem,
        SavedToRegistry,
        SavedToFileSystem,
        WorkflowClosed,
        WorkflowExecute
    }

    public class UIHelper : IDisposable
    {
        #region Private fields

        private SR.Connection registryConnection;

        /// <summary>
        /// Holds the workflow composer.
        /// </summary>
        private WorkflowComposer wfLoader;

        /// <summary>
        /// Holds the activity composer.
        /// </summary>
        private ActivityComposer actLoader;

        /// <summary>
        /// Filename of the opened file.
        /// </summary>
        string openedFileName;

        /// <summary>
        /// File location of the opened file.
        /// </summary>
        string openedFileLocation;

        /// <summary>
        /// The composer workflow model.
        /// </summary>
        private TridentWorkflowModel composerWorkflowModel;

        /// <summary>
        /// Condition DataStore for the if else control.
        /// </summary>
        private ConditionDataStore conditionStore;

        private FileSystemOperationsProxy fileSystemOperationProxy;

        private FileSystemOperationsProxy backup;

        #endregion

        #region Events

        /// <summary>
        /// Fired when the Import activity starts.
        /// </summary>
        public event EventHandler<WaitMessageEventArgs> AddWaitControl;

        /// <summary>
        /// Fired when the Import activity completes.
        /// </summary>
        public event EventHandler<EventArgs> RemoveWaitControl;

        /// <summary>
        /// Fired when activity import is completed to return the result and import status.
        /// </summary>
        public event EventHandler<ImportEventArgs> ActivityImported;

        /// <summary>
        /// Fired when workflow package import is completed to return the result and import status.
        /// </summary>
        public event EventHandler<ImportEventArgs> WorkflowPackageImported;

        /// <summary>
        /// Fired when workflow import is completed to return the result and import status.
        /// </summary>
        public event EventHandler<ImportEventArgs> WorkflowImported;
        /// <summary>
        /// Fired when cutom type import is completed to return the result and import status.
        /// </summary>
        public event EventHandler<ImportEventArgs> CustomTypeImported;

        #endregion

        #region Public properties

        /// <summary>
        /// The workflow composer.
        /// </summary>
        public WorkflowComposer WorkflowComposer
        {
            get
            {
                return this.wfLoader;
            }
        }

        /// <summary>
        /// Get and Set the Current Status of the Workflow in the Composer Window
        /// </summary>
        public string CurrentStatus { get; set; }

        /// <summary>
        /// This specifies whether the Current Workflow is committed to Primary Source.
        ///     TRUE = Changes in the wf is committed to Primary Source.
        ///     FALSE = Changes in the wf is NOT committed to Primary Source.
        /// </summary>
        public bool IsWFCommitted
        {
            get
            {
                return this.CurrentComposerControl.IsWFCommitted;
            }
            set
            {
                this.CurrentComposerControl.IsWFCommitted = value;
            }
        }

        /// <summary>
        /// This specifies the file name of the file opened from file system.
        /// </summary>
        public string OpenedFileName
        {
            get
            {
                return this.openedFileName;
            }
            set
            {
                this.openedFileName = value;
            }
        }

        /// <summary>
        /// This specifies the file name of the file opened from file system.
        /// </summary>
        public string OpenedFileLocation
        {
            get
            {
                return this.openedFileLocation;
            }
            set
            {
                this.openedFileLocation = value;
            }
        }

        /// <summary>
        /// Check if any workflow is loaded.
        /// </summary>
        public bool IsWorkflowOpen
        {
            get
            {
                if (this.wfLoader != null && this.ComposerWorkflowModel != null)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// Retrieves the name of the current workflow.
        /// </summary>
        public string GetCurrentWorkflowName
        {
            get
            {
                string workflowName = string.Empty;


                if (CurrentStatus.Equals(WorkflowStatus.NewWorkflow.ToString()))
                {
                    workflowName = "New Workflow";
                }
                else if (CurrentStatus.Equals(WorkflowStatus.OpenedFromFileSystem.ToString()))
                {
                    workflowName = this.openedFileName;
                }
                else
                {
                    if (this.ComposerWorkflowModel != null)
                    {
                        workflowName = this.ComposerWorkflowModel.Name;
                    }
                    else
                    {
                        workflowName = string.Empty;
                    }
                }

                return workflowName;
            }
        }

        /// <summary>
        /// Gets the label of the open workflow.
        /// </summary>
        public string CurrentWorkflowLabel
        {
            get
            {
                if (this.ComposerWorkflowModel != null)
                {
                    return this.ComposerWorkflowModel.VersionLabel;
                }
                return string.Empty;
            }
        }

        /// <summary>
        /// Current loaded wf ID.
        /// </summary>
        public Guid CurrentWorkflowId
        {
            get
            {
                if (this.wfLoader != null && this.ComposerWorkflowModel != null)
                {
                    return this.ComposerWorkflowModel.Id;
                }
                else
                {
                    return Guid.Empty;
                }
            }
        }

        /// <summary>
        /// Gets and sets the composer workflow model.
        /// </summary>
        public TridentWorkflowModel ComposerWorkflowModel
        {
            get
            {
                return this.composerWorkflowModel;
            }

            set
            {
                this.composerWorkflowModel = value;
            }
        }

        /// <summary>
        /// Gets and sets the Monitor workflow model.
        /// </summary>
        public TridentWorkflowModel MonitorWorkflowModel { get; set; }

        /// <summary>
        /// Gets and sets the current composer control.
        /// </summary>
        public ComposerControl CurrentComposerControl { get; set; }

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="tridentWindow">The window handle.</param>
        /// <param name="registryConn">Registry connection.</param>
        public UIHelper(SR.Connection registryConnection)
        {
            this.registryConnection = registryConnection;
            try
            {
                // Loads the Built In activities.
                actLoader = new ActivityComposer(registryConnection, false);
                wfLoader = new WorkflowComposer(registryConnection, actLoader);
                this.IntializeConditionDataStore();
            }
            catch (TridentCustomException ex)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(Application.Current.MainWindow, ex.CustomMessage.ToString());
            }

            if (null == wfLoader || null == actLoader)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(
                    Application.Current.MainWindow,
                    TridentResourceManager.GetString("ApllicationLoadError"));
                Environment.Exit(0);
            }
        }

        #endregion

        /// <summary>
        /// Close Workflow
        /// </summary>
        public void CloseWorkflow()
        {
            this.CurrentComposerControl.ClearUICache();
            this.IsWFCommitted = true;
            this.CurrentStatus = WorkflowStatus.WorkflowClosed.ToString();

            // Reset the current Workflow model.
            if (this.ComposerWorkflowModel != null)
            {
                this.ComposerWorkflowModel = null;
            }

            // Reset the current composer control Workflow model.
            if (this.CurrentComposerControl.CurrentWorkflowModel != null)
            {
                this.CurrentComposerControl.CurrentWorkflowModel = null;
            }

            // close the property pane when workflow is closed.
            this.CurrentComposerControl.RemovePropertyPaneOnChange();
        }

        /// <summary>
        /// Initialize the  application object for Condition DataStore
        /// </summary>
        private void IntializeConditionDataStore()
        {
            //If condition dictionary Initialize
            this.conditionStore = new ConditionDataStore();
        }

        /// <summary>
        /// Load a workflow on passing the ID. Called when a click happens on a workflow in the wf tree.
        /// </summary>
        /// <param name="workflowId">Id of the workflow.</param>
        public bool LoadWorkflowFromLeftPane(Guid workflowId)
        {
            if (workflowId == null)
            {
                throw new ArgumentException("Workflow Id not set.");
            }

            try
            {
                if (this.wfLoader != null)
                {
                    TridentWorkflowModel workflowModel = this.wfLoader.GetWorkflowModel(workflowId, false);
                    workflowModel.SwapSubsectionState += new EventHandler(this.OnSwapSubsectionState);
                    if (workflowModel != null)
                    {
                        try
                        {
                            this.CurrentComposerControl.LoadWorkflow(workflowModel, false);
                            this.ComposerWorkflowModel = workflowModel;
                        }
                        catch (TridentCustomException exp)
                        {
                            TridentMessageBox.ShowTridentErrorMessageBox(
                                Application.Current.MainWindow,
                                exp.Message);
                            this.CurrentComposerControl.LoadWorkflow(this.ComposerWorkflowModel, true);
                        }
                    }

                    this.IsWFCommitted = true;
                    return true;
                }

            }
            catch (TridentCustomException exp)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(Application.Current.MainWindow, exp.CustomMessage.ToString());
            }

            return false;
        }

        private void OnSwapSubsectionState(object sender, EventArgs e)
        {
            this.CurrentComposerControl.SwapSubsectionState(sender);
        }

        /// <summary>
        /// Open the workflow from the file system.
        /// </summary>
        /// <param name="filePath">
        /// File path of the xoml.
        /// </param>
        /// <param name="directoryPath">
        /// The path of the directory.
        /// </param>
        /// <returns>
        /// Status of the operation.
        /// </returns>
        public bool? OpenWorkflowFromFileSystem(Collection<FileReference> fileReferences)
        {
            bool? openStatus = null;

            // If the previously opened workflow is from filesystem. Keep the old appdomain as backup.
            // If the new open fails need to restore.
            if (this.CurrentStatus.Equals(WorkflowStatus.OpenedFromFileSystem.ToString(), StringComparison.OrdinalIgnoreCase))
            {
                this.backup = this.fileSystemOperationProxy;
            }

            // Open the import dialog to allow the user to select the files.
            ImportDialog importDialog = new ImportDialog(Application.Current.MainWindow, this.registryConnection, ImportType.OpenFromFileSystem);
            importDialog.ShowDialog();

            if (!importDialog.Canceled)
            {
                fileReferences.Clear();
                foreach (FileReference fileReference in importDialog.FileReferences)
                {
                    fileReferences.Add(fileReference);
                }

                // Create a proxy to create the model. This loads the assemblies in another appdomain and returns the workflow model.
                this.fileSystemOperationProxy = new FileSystemOperationsProxy(this.registryConnection);
                TridentWorkflowModel wfModel = this.fileSystemOperationProxy.OpenFromFile(fileReferences);

                // Set the parent-child relationships between models. This info is lost when serilization occurs across appdomains.
                TridentWorkflowModel.SetParentChildRelationship(wfModel);

                if (wfModel != null)
                {
                    try
                    {
                        this.CurrentComposerControl.LoadWorkflow(wfModel, false);
                        this.ComposerWorkflowModel = wfModel;

                        // If the operation is a success clear the backup.
                        this.ClearBackup();
                    }
                    catch (TridentCustomException exp)
                    {
                        TridentMessageBox.ShowTridentErrorMessageBox(Application.Current.MainWindow, exp.Message);

                        // Reload the old workflow.
                        this.CurrentComposerControl.LoadWorkflow(this.ComposerWorkflowModel, true);
                        this.ApplyBackup();
                    }

                    openStatus = true;
                }
                else
                {
                    openStatus = false;
                }
            }

            return openStatus;
        }

        /// <summary>
        /// Clear the proxy which is used to open workflow from filesystem.
        /// </summary>
        public void ClearOpenFromFileSystemDetails()
        {
            if (this.fileSystemOperationProxy != null)
            {
                this.fileSystemOperationProxy.Unload();
                this.fileSystemOperationProxy = null;
            }
        }

        /// <summary>
        /// Apply the backup proxy.
        /// </summary>
        public void ApplyBackup()
        {
            this.ClearOpenFromFileSystemDetails();
            this.fileSystemOperationProxy = this.backup;
        }

        /// <summary>
        /// Clear the backup proxy.
        /// </summary>
        private void ClearBackup()
        {
            if (this.backup != null)
            {
                this.backup.Unload();
                this.backup = null;
            }
        }

        /// <summary>
        /// Creates a new Sequential Workflow workflow  model
        /// and loads the Workflow.
        /// By Default it creates a Sequential Workflow Activity.
        /// </summary>
        public void CreateNewWorkflowActivity()
        {
            TridentWorkflowModel newWorkflow = new TridentWorkflowModel();

            // Get the Activity Model.
            newWorkflow.Root = actLoader.GetActivityModel("System.Workflow.Activities.SequentialWorkflowActivity");
            try
            {
                // Loads the newly created Workflow
                this.CurrentComposerControl.LoadWorkflow(newWorkflow, false);
                this.conditionStore.ClearDataStore();
                this.ComposerWorkflowModel = newWorkflow;
            }
            catch (TridentCustomException exp)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(
                    Application.Current.MainWindow,
                    exp.Message);

                // Reload the old wf.
                this.CurrentComposerControl.LoadWorkflow(this.ComposerWorkflowModel, true);
            }
        }

        /// <summary>
        /// Imports the files passed to create a Workflow for the Trident system.
        /// </summary>
        /// <param name="registryConnection">The registry connnection.</param>
        /// <returns>
        /// bool value indicating status of the import.
        /// </returns>
        public void ImportWorkflow(SR.Connection registryConnection)
        {
            ImportDialog importDialog = new ImportDialog(Application.Current.MainWindow, registryConnection, ImportType.Workflow);
            importDialog.ShowDialog();

            if (!importDialog.Canceled && this.wfLoader != null)
            {
                if (this.AddWaitControl != null)
                {
                    //Raise the show wait control event
                    this.AddWaitControl.Invoke(this, new WaitMessageEventArgs(TridentResourceManager.GetString("Importing")));
                }

                // Add the parameters to be sent to background worker.
                Hashtable param = new Hashtable();
                param.Add("WorkflowProperty", importDialog.WorkflowProperty);
                param.Add("FileReference", importDialog.FileReferences);

                // Create a background worker to import the workflow.
                BackgroundWorker worker = new BackgroundWorker();
                worker.DoWork += new DoWorkEventHandler(this.OnImportWorkflowInBackground);
                worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.OnWorkflowImported);
                worker.RunWorkerAsync(param);
            }
        }
        /// <summary>
        /// Occurs when the background worker thread starts its execution for workflow import.
        /// </summary>
        /// <param name="sender">
        /// Background worker thread.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnImportWorkflowInBackground(object sender, DoWorkEventArgs e)
        {
            Hashtable param = e.Argument as Hashtable;
            // store the success or failure of the import process.
            bool? importStatus = null;
            string message = string.Empty;
            if (param != null)
            {
                // Get all the required parameters.
                WorkflowProperties wfprop = param["WorkflowProperty"] as WorkflowProperties;
                Collection<FileReference> fileref = param["FileReference"] as Collection<FileReference>;

                try
                {
                    ImportWorkflowProxy importWorkflow = new ImportWorkflowProxy(
                                           wfprop,
                                           fileref,
                                           this.registryConnection,
                                           TridentAuthentication.LoggedUserInUserName);
                    Guid wfActivityID = importWorkflow.Execute();
                    importStatus = (wfActivityID != Guid.Empty);
                }
                catch (TridentCustomException ex)
                {
                    importStatus = false;
                    message += ex.Message;
                }
                catch (WorkflowValidationFailedException ex)
                {
                    importStatus = false;
                    StringBuilder errorString = new StringBuilder();
                    foreach (System.Workflow.ComponentModel.Compiler.ValidationError error in ex.Errors)
                    {
                        errorString.AppendLine(error.ErrorText);
                    }

                    message += errorString.ToString();
                }
                catch (Exception ex)
                {
                    importStatus = false;
                    message += ex.Message;
                }
                //Store the result in the param.
                param.Add("ImportStatus", importStatus);
                param.Add("Message", message);

                // store the param so that workercompleted event can access it.
                e.Result = param;
            }
        }

        /// <summary>
        /// Occurs after the worker process completed for workflow import.
        /// </summary>
        /// <param name="sender">
        /// Background worker thread.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnWorkflowImported(object sender, RunWorkerCompletedEventArgs e)
        {
            if (this.RemoveWaitControl != null)
            {
                // raise the remove wait control event.
                this.RemoveWaitControl.Invoke(this, new EventArgs());
            }

            // retrieve the result param
            Hashtable param = e.Result as Hashtable;

            if (param != null)
            {
                //retrieve the params
                bool? importStat = param["ImportStatus"] as bool?;
                string msg = param["Message"] as string;

                bool result = importStat.HasValue ? importStat.Value : false;

                //return the import result and the error message if any exception occurs.
                if (this.WorkflowImported != null)
                {
                    this.WorkflowImported.Invoke(this, new ImportEventArgs(msg, result));
                }
            }
        }

        /// <summary>
        /// Imports a workflow package.
        /// </summary>
        /// <returns>
        /// bool value indicating status of the import.
        /// </returns>
        public void ImportWorkflowPackage()
        {
            // Create a temp directory for package.
            string packageTempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());

            // Create an instance of import dialog.
            ImportDialog importDialog = new ImportDialog(Application.Current.MainWindow, registryConnection, ImportType.WorkflowPackage);
            importDialog.PackageTempDirectory = packageTempDir;
            importDialog.ShowDialog();

            if (!importDialog.Canceled && this.wfLoader != null)
            {
                if (this.AddWaitControl != null)
                {
                    //Raise the show wait control event
                    this.AddWaitControl.Invoke(this, new WaitMessageEventArgs(TridentResourceManager.GetString("Importing")));
                }

                // Add the parameters to be sent to background worker.
                Hashtable param = new Hashtable();
                param.Add("WorkflowProperty", importDialog.WorkflowProperty);
                param.Add("FileReference", importDialog.FileReferences);
                param.Add("PackagePath", importDialog.PackagePath);
                param.Add("TempPath", packageTempDir);

                // Create a background worker to import the workflow package.
                BackgroundWorker worker = new BackgroundWorker();
                worker.DoWork += new DoWorkEventHandler(this.OnImportWorkflowPackageInBackground);
                worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.OnWorkflowPackageImported);
                worker.RunWorkerAsync(param);
            }
        }
        /// <summary>
        /// Occurs when the background worker thread starts its execution for workflow package import.
        /// </summary>
        /// <param name="sender">
        /// Background worker thread.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnImportWorkflowPackageInBackground(object sender, DoWorkEventArgs e)
        {
            Hashtable param = e.Argument as Hashtable;
            // store the success or failure of the import process.
            bool? importStatus = null;
            string message = string.Empty;
            if (param != null)
            {
                // Get all the required parameters.
                WorkflowProperties wfprop = param["WorkflowProperty"] as WorkflowProperties;
                Collection<FileReference> fileref = param["FileReference"] as Collection<FileReference>;
                string path = param["PackagePath"] as string;

                try
                {
                    ImportWorkflowProxy importWorkflow = new ImportWorkflowProxy(
                            wfprop,
                            fileref,
                            this.registryConnection,
                            TridentAuthentication.LoggedUserInUserName);

                    Guid wfId = importWorkflow.Execute();
                    importStatus = wfId == Guid.Empty ? false : true;

                    if (importStatus.Value)
                    {
                        this.AddMetadata(wfId, path, false);
                    }
                }
                catch (TridentCustomException ex)
                {
                    importStatus = false;
                    message += ex.Message;
                }
                catch (WorkflowValidationFailedException ex)
                {
                    importStatus = false;
                    StringBuilder errorString = new StringBuilder();
                    foreach (System.Workflow.ComponentModel.Compiler.ValidationError error in ex.Errors)
                    {
                        errorString.AppendLine(error.ErrorText);
                    }

                    message += errorString.ToString();
                }
                catch (Exception ex)
                {
                    importStatus = false;
                    message += ex.Message;
                }

                //Store the result and exception message if caught in the param.
                param.Add("ImportStatus", importStatus);
                param.Add("Message", message);

                // store the param so that workercompleted event can access it.
                e.Result = param;
            }
        }

        /// <summary>
        /// Occurs after the worker process completed for workflow package import.
        /// </summary>
        /// <param name="sender">
        /// Background worker thread.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnWorkflowPackageImported(object sender, RunWorkerCompletedEventArgs e)
        {
            if (this.RemoveWaitControl != null)
            {
                // raise the remove wait control event.
                this.RemoveWaitControl.Invoke(this, new EventArgs());
            }

            // retrieve the result param
            Hashtable param = e.Result as Hashtable;

            if (param != null)
            {
                //retrieve the params
                bool? importStat = param["ImportStatus"] as bool?;
                string msg = param["Message"] as string;

                bool result = importStat.HasValue ? importStat.Value : false;

                //return the import result and the error message if any exception occurs.
                if (this.WorkflowPackageImported != null)
                {
                    this.WorkflowPackageImported.Invoke(this, new ImportEventArgs(msg, result));
                }

                try
                {
                    string tempPath = param["TempPath"] as string;
                    // Delete temp directory created.
                    Directory.Delete(tempPath, true);
                }
                catch (Exception ex)
                {
                    // Temp directory could not be deleted. Log and ignore.
                    Logger.Write(ex.Message);
                }
            }
        }

        /// <summary>
        /// Imports an activity into the system.
        /// </summary>
        /// <returns>
        /// True if successfully imported, False otherwise.
        /// </returns>
        public void ImportActivities()
        {
            // Get file references.
            ImportDialog importDialog = new ImportDialog(Application.Current.MainWindow, this.registryConnection, ImportType.Activity);
            importDialog.ShowDialog();

            // Import activities in a separate appdomain.
            if (!importDialog.Canceled && this.actLoader != null)
            {
                if (this.AddWaitControl != null)
                {
                    //Raise the show wait control event
                    this.AddWaitControl.Invoke(this, new WaitMessageEventArgs(TridentResourceManager.GetString("Importing")));
                }

                // Add the parameters to be sent to background worker.
                Hashtable param = new Hashtable();
                param.Add("FileReference", importDialog.FileReferences);

                // Create a background worker to import the activity.
                BackgroundWorker worker = new BackgroundWorker();
                worker.DoWork += new DoWorkEventHandler(this.OnImportActivityInBackground);
                worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.OnActivityImported);
                worker.RunWorkerAsync(param);
            }
        }

        /// <summary>
        /// Occurs when the background worker thread starts its execution for activity import.
        /// </summary>
        /// <param name="sender">
        /// Background worker thread.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnImportActivityInBackground(object sender, DoWorkEventArgs e)
        {
            Hashtable param = e.Argument as Hashtable;
            // store the success or failure of the import process.
            bool? importStatus = null;
            string message = string.Empty;
            if (param != null)
            {
                // Get all the required parameters.
                Collection<FileReference> importfileRef = param["FileReference"] as Collection<FileReference>;
                try
                {
                    ImportActivityProxy importProxy = new ImportActivityProxy(this.registryConnection, importfileRef, false);
                    importStatus = importProxy.Execute();
                }
                catch (TridentCustomException ex)
                {
                    importStatus = false;
                    message += ex.Message;
                }
                catch (Exception ex)
                {
                    importStatus = false;
                    message += ex.Message;
                }

                //Store the result and exception message if caught in the param.
                param.Add("ImportStatus", importStatus);
                param.Add("Message", message);

                // store the param so that workercompleted event can access it.
                e.Result = param;
            }
        }
        /// <summary>
        /// Occurs after the worker process completed for activity import.
        /// </summary>
        /// <param name="sender">
        /// Background worker thread.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnActivityImported(object sender, RunWorkerCompletedEventArgs e)
        {
            if (this.RemoveWaitControl != null)
            {
                // raise the remove wait control event.
                this.RemoveWaitControl.Invoke(this, new EventArgs());
            }

            // retrieve the result param
            Hashtable param = e.Result as Hashtable;

            if (param != null)
            {
                //retrieve the params
                bool? importStat = param["ImportStatus"] as bool?;
                string msg = param["Message"] as string;

                bool result = importStat.HasValue ? importStat.Value : false;

                //return the import result and the error message if any exception occurs.
                if (this.ActivityImported != null)
                {
                    this.ActivityImported.Invoke(this, new ImportEventArgs(msg, result));
                }
            }
        }

        /// <summary>
        /// Imports an activity specified in the collection into the system.
        /// </summary>
        /// <param name="references">
        /// Activity file references.
        /// </param>
        /// <param name="createNewVersionsOnNameMatch">True if new versions need to be created if the name matches. False otherwise.</param>
        /// <returns>
        /// True if successfully imported, False otherwise.
        /// </returns>
        public bool ImportActivities(Collection<FileReference> references, bool createNewVersionsOnNameMatch)
        {
            // Import activities in a separate appdomain.
            ImportActivityProxy importProxy = new ImportActivityProxy(
                this.registryConnection,
                references,
                createNewVersionsOnNameMatch);

            return (bool)importProxy.Execute();
        }

        /// <summary>
        /// Import custom type initializers from the path specified.
        /// </summary>
        /// <param name="dllPath">The path from where the custom types are to be imported.</param>
        public void ImportCustomTypes(string dllPath)
        {
            if (string.IsNullOrEmpty(dllPath))
            {
                throw new ArgumentNullException("dllPath");
            }

            if (this.AddWaitControl != null)
            {
                //Raise the show wait control event
                this.AddWaitControl.Invoke(this, new WaitMessageEventArgs(TridentResourceManager.GetString("Importing")));
            }

            // Add the parameters to be sent to background worker.
            Hashtable param = new Hashtable();
            param.Add("dllPath", dllPath);

            // Create a background worker to import the activity.
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(this.OnImportCustomTypeInBackground);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.ImportCustomTypeImported);
            worker.RunWorkerAsync(param);
        }

        /// <summary>
        /// Occurs when the background worker thread starts its execution for custom type import.
        /// </summary>
        /// <param name="sender">
        /// Background worker thread.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnImportCustomTypeInBackground(object sender, DoWorkEventArgs e)
        {
            Hashtable param = e.Argument as Hashtable;
            // store the status of the process.
            OperationStatus importStatus = OperationStatus.Fail;
            string message = string.Empty;
            string messageToAdd = TridentResourceManager.GetString("CustomTypeInitializerFailed");
            if (param != null)
            {
                // Get all the required parameters.
                string path = param["dllPath"] as string;
                bool? status = null;
                try
                {
                    ImportTypeInitializerProxy importTypeInitializer = new ImportTypeInitializerProxy(path, this.registryConnection);
                    importStatus = importTypeInitializer.Execute();
                    TypeProviderHelper.RefreshTypeInitializersCache();

                    status = importStatus.Equals(OperationStatus.Success) ? true : false;
                }
                catch (TridentCustomException ex)
                {
                    message += messageToAdd;
                    message += ex.Message;
                }
                catch (Exception ex)
                {
                    message += messageToAdd;
                    message += ex.Message;
                }

                if (importStatus == OperationStatus.Fail)
                {
                    message = messageToAdd;
                }
                else if (importStatus == OperationStatus.NoActionRequired)
                {
                    message = TridentResourceManager.GetString("AllAssembliesExist");
                }
                else if (importStatus == OperationStatus.Success)
                {
                    message = TridentResourceManager.GetString("ImportCustomTypeInitializersSuccess");
                }

                //Store the result and exception message if caught in the param.
                param.Add("Status", status);
                param.Add("Message", message);

                // store the param so that workercompleted event can access it.
                e.Result = param;
            }
        }

        /// <summary>
        /// Occurs after the worker process completed for custom type import.
        /// </summary>
        /// <param name="sender">
        /// Background worker thread.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void ImportCustomTypeImported(object sender, RunWorkerCompletedEventArgs e)
        {
            if (this.RemoveWaitControl != null)
            {
                // raise the remove wait control event.
                this.RemoveWaitControl.Invoke(this, new EventArgs());
            }

            // retrieve the result param
            Hashtable param = e.Result as Hashtable;

            if (param != null)
            {
                //retrieve the params
                bool? importStat = param["Status"] as bool?;
                string msg = param["Message"] as string;

                bool result = importStat.HasValue ? importStat.Value : false;

                //return the import result and the error message if any exception occurs.
                if (this.CustomTypeImported != null)
                {
                    this.CustomTypeImported.Invoke(this, new ImportEventArgs(msg, result));
                }
            }
        }

        /// <summary>
        /// Retreive all the workflow categories.
        /// </summary>
        /// <returns></returns>
        public Collection<WorkflowCategoryModel> FetchWorkflowCategories()
        {
            return this.wfLoader.ReadAllWorkflowCategories(TridentAuthentication.LoggedInUser);
        }

        /// <summary>
        /// Retreive all the activity categories.
        /// </summary>
        /// <returns></returns>
        public Collection<ActivityCategoryModel> FetchActivityCategories()
        {
            return this.actLoader.ReadAllActivityCategories();
        }

        /// <summary>
        /// This function is used to load the activity model into the Base UI.
        /// </summary>
        /// <param name="actModel">Activity Model.</param>
        public static TridentUIElement LoadActivity(SimpleActivityModel activityModel, Dictionary<string, DataTypeModel> dataTypes)
        {
            return new WorkflowActivityElement(activityModel, dataTypes);
        }

        #region Executor

        /// <summary>
        /// Creates the workflow job in the registry
        /// </summary>
        /// <param name="jobDetails">Details of the job.</param>
        /// <param name="machineId">The machine identifier.</param>
        /// <param name="isDebug">True if the job has to be run in Debug mode, False otherwise.</param>
        public Guid CreateWorkflowJob(JobDetailModel jobDetails, Guid machineId, bool isDebug)
        {
            // Create the Job based on the saved Workflow.
            SR.Activity wfActivity = SR.Activity.Load(jobDetails.WorkflowId, this.registryConnection);

            SR.Machine selectedNode = SR.Machine.Load(machineId, this.registryConnection);

            // Create Job in registry. This would be used by the WFExecutor service to run the Job.
            if (wfActivity.IsWorkflow)
            {
                SR.Job currentJob = new JobComposer(this.registryConnection).ExecuteWFOnMachine(jobDetails, selectedNode, null, isDebug);
                return (currentJob as SR.IObject).ID;
            }

            return Guid.Empty;
        }

        /// <summary>
        /// Abort the currently executing job
        /// </summary>
        public void AbortCurrentJob(Guid jobId)
        {
            try
            {
                SR.Job job = SR.Job.Load(jobId, this.registryConnection);
                if (job.Status == SR.JobStatus.Waiting)
                {
                    job.Status = SR.JobStatus.Aborted;
                }
                else
                {
                    job.Status = SR.JobStatus.StopPending;
                }

                job.ErrorMessage = string.Format(CultureInfo.InvariantCulture, "{0} [{1} ({2})].", ScientificWorkFlow.JobTerminatedByUserMessage,
                      TridentAuthentication.LoggedUserInUserName, TridentAuthentication.LoggedUserRole.ToString());
                job.Save();
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleUIException(ex);
            }
        }

        #endregion

        #region Save Workflow

        /// <summary>
        /// Save workflow with the Name Specified.
        /// </summary>
        /// <param name="workflowName">Name of the Workflow.</param>
        /// <returns>eResearch.Acitivity of the workflow Created.</returns>
        public bool CreateWorkflowInRegistry(string workflowName, Collection<FileReference> references)
        {
            if (string.IsNullOrEmpty(workflowName))
            {
                throw new ArgumentException("workflow name null");
            }

            if (references == null)
            {
                throw new ArgumentNullException("references");
            }

            bool workflowCreated = false;
            if (wfLoader != null && this.ComposerWorkflowModel != null)
            {
                if (this.CurrentStatus == WorkflowStatus.OpenedFromFileSystem.ToString())
                {
                    // Save the workflow in a different appdomain. As assemblies need to be loaded.
                    SaveWorkflowFromFileSystemProxy saveProxy = new SaveWorkflowFromFileSystemProxy(this.registryConnection);
                    TridentWorkflowModel savedWorkflowModel = saveProxy.Execute(references, this.composerWorkflowModel, workflowName, TridentAuthentication.LoggedUserInUserName);

                    if (savedWorkflowModel != null)
                    {
                        workflowCreated = true;

                        this.ComposerWorkflowModel = savedWorkflowModel;
                        this.CurrentComposerControl.CurrentWorkflowModel = savedWorkflowModel;
                        this.CurrentComposerControl.LoadWorkflow(savedWorkflowModel, false);
                    }
                    this.ClearOpenFromFileSystemDetails();
                }
                else
                {
                    this.ActivityExistsInRegistry();
                    wfLoader.SaveWorkflowInRegistry(this.ComposerWorkflowModel, references, TridentAuthentication.LoggedUserInUserName);

                    workflowCreated = true;
                }
            }

            return workflowCreated;
        }

        /// <summary>
        /// Checks whether the refered activity present in the registry or not.
        /// </summary>
        private void ActivityExistsInRegistry()
        {
            foreach (string key in ComposerWorkflowModel.ModelsHashtable.Keys)
            {
                BaseModel model = ComposerWorkflowModel.ModelsHashtable[key] as BaseModel;

                try
                {
                    if (model != null)
                    {
                        SR.Activity activity = SR.Activity.Load(model.Id, this.registryConnection);

                        if (activity == null || activity.IsDeleted)
                        {
                            throw new TridentCustomException(TridentErrorConstants.ErrorNumber90000036,
                                string.Format(CultureInfo.InvariantCulture, TridentResourceManager.GetString("ActivityNotFound"), model.Name));
                        }
                    }
                }
                catch (SR.RecordNotFoundException)
                {
                    throw new TridentCustomException(TridentErrorConstants.ErrorNumber90000036,
                        string.Format(CultureInfo.InvariantCulture, TridentResourceManager.GetString("ActivityNotFound"), model.Name));
                }
                catch (TridentCustomException tridentException)
                {
                    throw new TridentCustomException(tridentException.Message, tridentException);
                }
                catch (Exception ex)
                {
                    TridentErrorHandler.HandleUIException(ex);
                }
            }
        }

        /// <summary>
        /// This function is used to save(update) the workflow into the registry.
        /// Save Workflow with ID passed as parameter.
        /// </summary>
        /// <param name="workflowID">GUID of the Workflow.</param>
        /// <returns>eResearch.Acitivity of the workflow Updated.</returns>
        public void UpdateWorkflow()
        {
            if (wfLoader != null && this.ComposerWorkflowModel != null)
            {
                this.ActivityExistsInRegistry();
                SR.Activity newVersion = wfLoader.UpdateWorkflowInRegistry(this.ComposerWorkflowModel, new Collection<FileReference>(), TridentAuthentication.LoggedUserInUserName);

                // Assign the Id of the new version for the workflow model.
                this.composerWorkflowModel.Id = (newVersion as SR.IObject).ID;
                this.composerWorkflowModel.VersionLabel = string.Empty;
            }
        }

        /// <summary>
        /// This is used to save the Workflow into local File System.
        /// </summary>
        /// <param workflowName="fileName">Full path of the File.</param>
        /// <returns>Whether file has saved successfully or not.</returns>
        public bool SaveWorkflow(string fileName)
        {
            StringBuilder errorString = new StringBuilder();
            bool errorInSave = false;
            try
            {
                if (this.CurrentStatus.Equals(WorkflowStatus.OpenedFromFileSystem.ToString(), StringComparison.OrdinalIgnoreCase))
                {
                    if (this.fileSystemOperationProxy == null)
                    {
                        this.fileSystemOperationProxy = new FileSystemOperationsProxy(this.registryConnection);
                    }

                    this.fileSystemOperationProxy.SaveToFile(fileName, this.composerWorkflowModel);
                }
                else
                {
                    FileSystemOperationsProxy saveToFilesystem = null;
                    try
                    {
                        // Saves the Workflow into the File.
                        saveToFilesystem = new FileSystemOperationsProxy(this.registryConnection);
                        saveToFilesystem.SaveToFile(fileName, this.composerWorkflowModel);
                    }
                    finally
                    {
                        if (saveToFilesystem != null)
                        {
                            saveToFilesystem.Unload();
                        }
                    }
                }
            }
            catch (WorkflowValidationFailedException validationException)
            {
                foreach (System.Workflow.ComponentModel.Compiler.ValidationError error in validationException.Errors)
                {
                    errorString.AppendLine(error.ErrorText);
                }

                errorInSave = true;
            }
            catch (TridentCustomException ex)
            {
                errorInSave = true;
                throw new TridentCustomException(ex.Message, ex);
            }
            finally
            {
                if (errorInSave && File.Exists(fileName))
                {
                    try
                    {
                        File.Delete(fileName);
                    }
                    catch (IOException) { }
                }
            }

            if (!string.IsNullOrEmpty(errorString.ToString()))
            {
                TridentErrorHandler.ThrowTridentCustomException(
                    TridentErrorConstants.ErrorNumber9000009,
                    errorString.ToString());
            }
            return true;
        }

        /// <summary>
        /// Retrieve the file name from the path specified.
        /// </summary>
        /// <param name="fullPath">Path of the file.</param>
        /// <returns>The file name without extension.</returns>
        public static string GetFileNameFromPath(string fullPath)
        {
            string fileName = string.Empty;
            try
            {
                fileName = Path.GetFileNameWithoutExtension(fullPath);
                return fileName;
            }
            catch (ArgumentException)
            {
                return string.Empty;
            }
        }

        #endregion

        /// <summary>
        /// Read the info for the custom datatypes from the local XML.
        /// </summary>
        public static void ReadUserDataTypeXml(Dictionary<String, DataTypeModel> dataTypes)
        {
            string xmlPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                               UIDesignerCodeConstants.DATATYPEXMLPATH);

            try
            {
                if (File.Exists(xmlPath))
                {
                    XmlDocument customDataTypes = XmlUtilities.GetXmlFromLocation(xmlPath);
                    DataTypesComposer.CreateDataTypesFromXml(customDataTypes, dataTypes, false, false);
                }
            }
            catch (XmlException ex)
            {
                // Log the exception.
                TridentErrorHandler.HandleUIException(ex);
            }
        }

        /// <summary>
        /// Save the user specific datatype Xml.
        /// </summary>
        public static void SaveUserDataTypeXml(Dictionary<String, DataTypeModel> dataTypes)
        {
            if (dataTypes != null)
            {
                try
                {
                    #region Create directory
                    string basePath1 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                                          UIDesignerCodeConstants.BASEPATH1);
                    if (!Directory.Exists(basePath1))
                    {
                        Directory.CreateDirectory(basePath1);
                    }

                    string basePath2 = Path.Combine(basePath1, UIDesignerCodeConstants.BASEPATH2);

                    if (!Directory.Exists(basePath2))
                    {
                        Directory.CreateDirectory(basePath2);
                    }
                    #endregion

                    XmlDocument dataTypesXml = DataTypesComposer.SaveCustomDataTypesToXml(dataTypes);
                    string xmlPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                        UIDesignerCodeConstants.DATATYPEXMLPATH);

                    XmlUtilities.WriteXmlAtLocation(dataTypesXml, xmlPath);
                }
                catch (XmlException ex)
                {
                    // Log error.
                    TridentErrorHandler.HandleUIException(ex);
                }
                catch (UnauthorizedAccessException ex)
                {
                    // Log error.
                    TridentErrorHandler.HandleUIException(ex);
                }
                catch (IOException ex)
                {
                    // Log error.
                    TridentErrorHandler.HandleUIException(ex);
                }
            }
        }

        /// <summary>
        /// This function is used to load the remote monitoring window.
        /// </summary>
        /// <param name="jobID">Job Id.</param>
        public static void DisplayMonitorWindow(Guid jobId)
        {
            // Create the WF Processes
            Process wfProcess = new Process();
            ProcessStartInfo pi = new ProcessStartInfo();
            pi.FileName = ConfigurationManager.AppSettings["RemoteMonitoringApllication"];

            StringBuilder arguments = new StringBuilder();
            arguments.Append("-job ");
            arguments.Append(jobId.ToString());

            pi.Arguments = arguments.ToString();
            pi.WindowStyle = ProcessWindowStyle.Normal;

            wfProcess.StartInfo = pi;

            wfProcess.Start();
        }

        /// <summary>
        /// Label the workflow.
        /// </summary>
        /// <param name="workflowId">The Id of the workflow to be labelled.</param>
        /// <param name="label">The label.</param>
        public void LabelWorkflow(Guid workflowId, string label)
        {
            if (workflowId.Equals(Guid.Empty))
            {
                throw new ArgumentNullException("workflowId");
            }
            if (string.IsNullOrEmpty(label))
            {
                throw new ArgumentNullException("label");
            }
            try
            {
                SR.Activity workflow = SR.Activity.Load(workflowId, this.registryConnection);
                workflow.VersionLabel = label;
                workflow.Save();
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(ex,
                    TridentErrorConstants.ErrorNumber90000031,
                    TridentResourceManager.GetString("WorkflowLabelingFailed"));
            }
        }

        /// <summary>
        /// Validate the label.
        /// </summary>
        /// <param name="categoryId">The category to which the workflow belongs to.</param>
        /// <param name="label">The workflow label</param>
        /// <param name="workflowName">The workflow name.</param>
        /// <returns>True if the label is valid. False otherwise.</returns>
        public bool ValidateLabel(Guid categoryId, string label, string workflowName)
        {
            try
            {
                SR.Namespace category = SR.Namespace.Load(categoryId, this.registryConnection);
                Collection<SR.Activity> activities = category.GetWorkflows(workflowName, label);
                if (activities != null && activities.Count > 0)
                {
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(ex,
                     TridentErrorConstants.ErrorNumber90000032,
                     TridentResourceManager.GetString("WorkflowLabelingFailed"));
            }
            return false;
        }

        /// <summary>
        /// Copies parameter assignment from OPC workflow activity to corresponding SQL workflow activity.
        /// </summary>
        /// <param name="sqlActSequence">
        /// Activity sequence of the SQL workflow activity.
        /// </param>
        /// <param name="opcWfModel">
        /// Workflow model for the OPC activity.
        /// </param>
        private static void CopyParameterAssignment(ActivitySequence sqlActSequence, TridentWorkflowModel opcWfModel)
        {
            // Check if this activity sequence is contained in the OPC workflow model.
            if (opcWfModel.ModelsHashtable.ContainsKey(sqlActSequence.Name))
            {
                BaseModel opcBaseModel = opcWfModel.ModelsHashtable[sqlActSequence.Name] as BaseModel;
                if (opcBaseModel != null)
                {
                    // Iterate through all the input parameters to check for workflow inputs.
                    foreach (ParameterDescriptionModel opcParamModel in opcBaseModel.InputParameters)
                    {
                        if (opcParamModel.IsMandatoryForWorkflow)
                        {
                            // Workflow input found in OPC package, copy it to SQL activity.
                            ParameterAssignment paramAssign = sqlActSequence.ParameterAssignments.FirstOrDefault(
                                item => item.ActivityParameter.Name.Equals(opcParamModel.Name, StringComparison.OrdinalIgnoreCase) &&
                                item.ActivityParameter.Direction == DirectionType.Input);
                            if (paramAssign != null)
                            {
                                paramAssign.RequiredForWorkflow = true;
                                paramAssign.Save();
                            }
                        }
                    }

                    // Iterate through all the output parameters to check for workflow output.
                    foreach (ParameterDescriptionModel opcParamModel in opcBaseModel.OutputParameters)
                    {
                        if (opcParamModel.IsFinal)
                        {
                            // Workflow output found in OPC package, copy it to SQL activity.
                            ParameterAssignment paramAssign = sqlActSequence.ParameterAssignments.FirstOrDefault(
                                item => item.ActivityParameter.Name.Equals(opcParamModel.Name, StringComparison.OrdinalIgnoreCase) &&
                                item.ActivityParameter.Direction == DirectionType.Output);
                            if (paramAssign != null)
                            {
                                paramAssign.IsFinal = true;
                                paramAssign.Type = opcParamModel.OutputType;
                                paramAssign.OutputLabel = opcParamModel.OutputLabel;
                                paramAssign.RequiredForWorkflow = true;
                                paramAssign.Save();
                            }
                        }
                    }
                }
            }

            // Iterate recursively through all the child activity sequences.
            foreach (ActivitySequence childSequence in sqlActSequence.Children)
            {
                UIHelper.CopyParameterAssignment(childSequence, opcWfModel);
            }
        }

        /// <summary>
        /// Generates the OPC connection.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns>OPC connection.</returns>
        private static OPCConnection GenerateOPCConnection(string fileName)
        {
            OPCConnection opcConnection = null;
            try
            {
                IConnMgrInfoParams opcParam = OPCConnectionParameters.CreateConnection("testName", fileName);
                ConnectionManager connMgr = ConnectionManager.Create(ConnectionManager.CancelBehavior.ThrowException);
                opcConnection = connMgr.OpenConnection(opcParam) as OPCConnection;
            }
            catch (Exception exp)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                   exp,
                   TridentErrorConstants.ErrorNumber1000000045,
                   exp.Message);
            }

            return opcConnection;
        }

        /// <summary>
        /// Add metadata to workflow from package. 
        /// Metadata like subsection and parameter assignment info are added to the workflow.
        /// </summary>
        /// <param name="importedWorkflowId">The Id of the workflow imported.</param>
        /// <param name="packagePath">The path of the package.</param>
        /// <param name="firstTimeInstall">True if this is first time install, False otherwise.</param>
        public void AddMetadata(Guid importedWorkflowId, string packagePath, bool firstTimeInstall)
        {
            Connection opcConnection = null;
            try
            {
                if (firstTimeInstall)
                {
                    Activity activity =
                        Activity.CommonSearches.FindActivityInRegistry("TridentBasicActivities.Activities.SubSection", this.registryConnection);
                    if (activity != null)
                    {
                        ActivityMetadata subsectionMetadata = new ActivityMetadata(activity);
                        TridentModel.SubsectionMetadata.Data = subsectionMetadata;
                    }
                }

                // Load the workflow imported to the registry. 
                Activity importedActivity = Activity.Load(importedWorkflowId, this.registryConnection);

                // Create an opc connection to retrieve metadata from the package.
                opcConnection = GenerateOPCConnection(packagePath);

                // Load all the activities and search for a workflow. Ideally there will only be one.
                List<Activity> allActivities = Activity.LoadAll(opcConnection);
                Activity wf = allActivities.FirstOrDefault(a => a.IsWorkflow);

                // Create a workflow model out of the workflow in the package. Makes it easier to find the subsections and parameter assignments.
                TridentWorkflowModel wfModel = new TridentWorkflowModel(wf, new ModelFactory(this.actLoader.ActivityMetadataList, this.actLoader.SupportedActivityCollection));

                // Merge subsections into the workflow recently imported.
                Activity subsectionActivity = Activity.Load(TridentModel.SubsectionMetadata.Data.Id, this.registryConnection);
                this.MergeSubsections(wfModel.Root, importedActivity.BaseSequence.ActivitySequences[0], importedActivity.BaseSequence.ActivitySequences[0], subsectionActivity);
                importedActivity.ActivitySequences.Refresh();

                // Merge the parameter assignments.
                UIHelper.CopyParameterAssignment(importedActivity.BaseSequence, wfModel);
            }
            catch (TridentCustomException)
            {
                throw;
            }
            catch (Exception ex)
            {
                TridentErrorHandler.ProcessKnownExceptionsInBusinessLogic(ex);
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                   ex,
                   TridentErrorConstants.ErrorNumber1000000075,
                   TridentResourceManager.GetString("PackageMetadataMergeError"));
            }
            finally
            {
                // Close the opc connection.
                if (opcConnection != null)
                {
                    opcConnection.Close();
                }
            }
        }

        /// <summary>
        /// Add subsection metadata into the workflow imported through the package.
        /// </summary>
        ///<param name="activityModel">The activity model to be merged.</param>
        ///<param name="activitySequence">The corresponding activity sequence from the imported workflow.</param>
        ///<param name="parentSequence">The sequence to which the subsection must be added if there is any.</param>
        ///<param name="subsectionActivity">The activity represeting a subsection activity. Static object retrieved from registry.</param>
        private void MergeSubsections(BaseModel activityModel, ActivitySequence activitySequence, ActivitySequence parentSequence, Activity subsectionActivity)
        {
            // If the encountered activity is a subsection then insert a subsection into the recently imported workflow at the required position.
            if (activityModel.IsSubsectionModel)
            {
                TridentSubsectionModel subsectionModel = activityModel as TridentSubsectionModel;

                // Create the subsection at the required position.
                ActivitySequence subsectionSequence = ActivitySequence.Create(parentSequence, subsectionActivity, this.registryConnection);
                subsectionSequence.Name = subsectionModel.UniqueId;
                subsectionSequence.Position = subsectionModel.IndexOfModel;
                subsectionSequence.Save();

                // Set parameter assignments for the subsection.(Label and the description)
                CreateParameterAssignments(subsectionActivity, subsectionSequence, subsectionModel);

                // Iterate through the child elements of the subsection and add set the parent element as the subsection.
                foreach (BaseModel baseModel in subsectionModel.Children)
                {
                    if (!baseModel.IsSubsectionModel)
                    {
                        activitySequence.ActivitySequences.GetEnumerator();
                        ActivitySequence childSequence = activitySequence.ActivitySequences.FirstOrDefault(s => s.Name.Equals(baseModel.UniqueId));
                        childSequence.Parent = subsectionSequence;
                        childSequence.Position = baseModel.IndexOfModel;
                        childSequence.Save();

                        // If activity is a composite activity recurse further.
                        if (baseModel.IsCompositeActivityModel)
                        {
                            MergeSubsections(baseModel, childSequence, childSequence, subsectionActivity);
                        }
                    }
                    else
                    {
                        // If the child activity itself is another subsection then do not change the base.
                        MergeSubsections(baseModel, activitySequence, subsectionSequence, subsectionActivity);
                    }
                }
            }
            else if (activityModel.IsCompositeActivityModel)
            {
                CompositeActivityModel compositeActivityModel = activityModel as CompositeActivityModel;
                activitySequence.Position = compositeActivityModel.IndexOfModel;
                activitySequence.Save();
                foreach (BaseModel baseModel in compositeActivityModel.Children)
                {
                    if (!baseModel.IsSubsectionModel)
                    {
                        // If not subsection then recurse through the children.
                        ActivitySequence childSequence = activitySequence.Children.Find(s => s.Name.Equals(baseModel.UniqueId));
                        MergeSubsections(baseModel, childSequence, childSequence, subsectionActivity);
                    }
                    else
                    {
                        // If subsection then recurse.
                        MergeSubsections(baseModel, activitySequence, activitySequence, subsectionActivity);
                    }
                }
            }
            else
            {
                activitySequence.Position = activityModel.IndexOfModel;
                activitySequence.Save();
            }
        }

        /// <summary>
        /// Create parameter assignments for the subsection.
        /// </summary>
        /// <param name="subsectionActivity">The activity represeting a subsection activity. Static object retrieved from registry.</param>
        /// <param name="sequence">The subsection sequence.</param>
        /// <param name="subsectionModel">Corresponding subsection model from which the parameter details will be taken.</param>
        private void CreateParameterAssignments(Activity subsectionActivity, ActivitySequence sequence, BaseModel subsectionModel)
        {
            // For all the parameters in the activity assign parameter assignments.
            foreach (ActivityParameter param in subsectionActivity.ActivityParameters)
            {
                // Create parameter assignments.
                ParameterAssignment paramAssignment = ParameterAssignment.Create(param, sequence, this.registryConnection);
                ParameterDescriptionModel paramDescriptionModel = subsectionModel.InputParameters.First(p => p.Name.Equals(param.Name));

                // Assign the value.
                if (paramDescriptionModel.Value != null)
                {
                    paramAssignment.CurrentValue = paramDescriptionModel.Value.ToString();
                }
                paramAssignment.Save();
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// The bulk of the clean-up code is implemented in Dispose(bool)
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.WorkflowComposer != null)
                {
                    this.WorkflowComposer.Dispose();
                }
            }
        }

        #endregion
    }
}
